package org.hry.socket.nio;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;

public class SocketClient {
    public static void main(String[] args) throws IOException {
        CountDownLatch downLatch = new CountDownLatch(1);
        IntStream.range(0, 1).forEach(v -> new Thread(() -> {
            try {
                downLatch.await();
                SocketChannel socket = SocketChannel.open();
                socket.configureBlocking(false);
                Selector selector = Selector.open();
                socket.register(selector, SelectionKey.OP_CONNECT);
                socket.connect(new InetSocketAddress("localhost", 8889));
                while (true) {
                    if (socket.isOpen()) {
                        // 在注册的键中选择已准备就绪的事件
                        selector.select();
                        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                        while (iterator.hasNext()) {
                            SelectionKey key = iterator.next();
                            iterator.remove();
                            if (key.isConnectable()) {
                                while (!socket.finishConnect()) {
                                    System.out.println("连接中");
                                }
                                socket.register(selector, SelectionKey.OP_READ);
                            }
                            //控制台监听到有输入，注册OP_WRITE,然后将消息附在attachment中
                            if (key.isWritable()) {
                                //发送消息给服务端
                                socket.write((ByteBuffer) key.attachment());
                            /*
	                            已处理完此次输入，但OP_WRITE只要当前通道输出方向没有被占用
	                            就会准备就绪，select()不会阻塞（但我们需要控制台触发,在没有输入时
	                            select()需要阻塞），因此改为监听OP_READ事件，该事件只有在socket
	                            有输入时select()才会返回。
                            */
                                socket.register(selector, SelectionKey.OP_READ);
                                System.out.println("============================");
                            }
                            //处理输入事件
                            if (key.isReadable()) {

                                //捕获异常，因为在服务端关闭后会发送FIN报文，会触发read事件，但连接已关闭,此时read()会产生异常
                                try {
                                    ByteBuffer byteBuffer = ByteBuffer.allocate(4);
                                    socket.read(byteBuffer);


//                                    int anInt = byteBuffer;
//                                    ByteBuffer allocate = ByteBuffer.allocate(anInt);
//
//                                    int read = socket.read(allocate);
//                                    System.out.println("接收到來自服务器的消息\t");
//                                    System.out.println(allocate);

                                } catch (IOException e) {
                                    System.out.println("服务器异常，请联系客服人员!正在关闭客户端.........");
                                    key.cancel();
                                    socket.close();
                                }
                                System.out.println("=========================================================");
                            }
                        }
                    } else {
                        break;
                    }
                }
            } catch (Exception e) {

            }
        }).start());
        downLatch.countDown();
    }
}
